package org.xx.armory.db.junit;

import org.xx.armory.commons.SysUtils;
import org.xx.armory.db.DbException;

import javax.xml.stream.XMLStreamException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

/**
 * 对数据库进行单元测试的工具类。
 *
 * @author Haart
 */
public interface DbUnitHelper {
    /**
     * 从XML文件中加载数据
     *
     * @param tableName
     *         数据库表名
     * @param stream
     *         包含XML文件的流
     * @throws IllegalArgumentException
     *         如果参数{@code tableName}是{@code null}或者只包含空白字符，或者参数{@code stream}是{@code null}。
     * @throws XMLStreamException
     *         如果加载文件时出现错误。
     */
    void readXml(
            String tableName,
            InputStream stream
    )
            throws XMLStreamException;

    /**
     * 从数据库中加载表的元数据
     *
     * @param tableName
     *         数据库表名
     * @throws IllegalArgumentException
     *         如果参数{@code tableName}是{@code null}或者只包含空白字符，或者参数{@code stream}是{@code null}。
     * @throws DbException
     *         如果加载表元数据时出现数据访问错误。
     */
    void loadMetaData(
            String tableName
    );

    /**
     * 加载指定表的所有记录。
     *
     * @param tableName
     *         数据库表名。
     * @throws IllegalArgumentException
     *         如果参数{@code tableName}是{@code null}或者只包含空白字符，或者参数{@code stream}是{@code null}。
     * @throws DbException
     *         如果加载数据出错，比如表不存在，表的名字不合法等。
     */
    void load(
            String tableName
    );

    /**
     * 加载指定表的符合条件的所有记录。
     *
     * @param tableName
     *         数据库表名。
     * @param predicate
     *         条件, 必须是合法的SQL表达式，可以是{@code null}或者空字符串，表示加载全部记录。
     * @throws DbException
     *         如果加载数据出错，比如表不存在，表的名字不合法，参数{@code predicate}不是有效的SQL表达式等等。
     */
    void load(
            String tableName,
            String predicate
    )
            throws DbException;

    /**
     * 将指定表的所有记录写入XML文件。
     *
     * @param tableName
     *         数据库表名
     * @param stream
     *         写入XML文件的输出流。
     * @throws IllegalArgumentException
     *         如果参数{@code tableName}是{@code null}或者只包含空白字符，或者参数{@code stream}是{@code null}。
     * @throws XMLStreamException
     *         如果写入XML文件时出现错误。
     * @throws DbException
     *         如果查询数据出错，比如表不存在，表的名字不合法等等。
     */
    void writeXml(
            String tableName,
            OutputStream stream
    )
            throws XMLStreamException;

    /**
     * 执行DDL命令。
     *
     * @param sql
     *         待执行的DDL命令。
     * @throws DbException
     *         如果执行SQL命令时出现错误。
     */
    void ddl(
            String sql
    )
            throws DbException;

    /**
     * 将所有从XML文件中加载的记录写入到数据库表。
     *
     * @param clean
     *         写入时是否删除原有的所有记录。
     * @throws DbException
     *         如果执行SQL命令时出现错误。
     * @see #clearAll()
     */
    void insertAll(
            boolean clean
    );

    /**
     * 删除所有被加载了元数据的数据库表中的所有记录。
     *
     * <p>
     * <strong>只有通过{@link #loadMetaData(String)}加载了元数据的表的记录才会被删除！</strong>
     * </p>
     *
     * @throws DbException
     *         如果执行SQL命令时出现错误。
     * @see #loadMetaData(String)
     */
    void clearAll();

    /**
     * 执行SQL查询并返回第一行查询结果, 如果没有任何查询结果则返回不包含任何元素的数组。
     *
     * @param sql
     *         待执行的SQL命令。
     * @param parameters
     *         SQL命令中包含的参数。
     * @return 第一行执行结果。
     * @throws DbException
     *         如果执行SQL命令时出现错误。
     */
    Object[] executeQuery(
            String sql,
            Object... parameters
    );

    /**
     * 执行SQL查询并返回包含所有查询结果的列表, 如果没有任何查询结果则返回不包含任何元素的列表。
     *
     * @param sql
     *         待执行的SQL命令。
     * @param parameters
     *         SQL命令中包含的参数。
     * @return 包含所有查询结果的列表。
     * @throws DbException
     *         如果执行SQL命令时出现错误。
     */
    List<Object[]> executeQueryList(
            String sql,
            Object... parameters
    );

    /**
     * 数据库表的列
     *
     * @author Haart
     */
    final class Column {
        private final String name;
        private final int type;

        /**
         * 构造数据库表的列
         *
         * @param name
         *         列名
         * @param type
         *         列类型
         */
        public Column(
                String name,
                int type
        ) {
            this.name = name;
            this.type = type;
        }

        /**
         * 获取列名
         *
         * @return the name
         */
        public String getName() {
            return this.name;
        }

        /**
         * 获取列类型
         *
         * @return the type
         */
        public int getType() {
            return this.type;
        }
    }

    /**
     * 数据库表的一条记录
     *
     * @author Haart
     */
    final class Row {
        private final String[] values;

        /**
         * 构造数据库表的一条记录
         *
         * @param values
         *         该记录包含的所有数据
         */
        public Row(
                String[] values
        ) {
            this.values = SysUtils.clone(values);
        }

        /**
         * 获取指定列的值(从0开始)
         *
         * @param index
         *         列下标
         * @return 指定列的值
         * @throws ArrayIndexOutOfBoundsException
         *         如果列下标溢出。
         */
        public final String get(
                int index
        ) {
            return this.values[index];
        }

        /**
         * 获取每条记录包含的数据的个数。
         *
         * @return 每条记录包含的数据的个数。
         */
        public final int size() {
            return this.values.length;
        }
    }
}
